package com.codeanalysissys.backend.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.codeanalysissys.backend.entity.UploadFile;
import com.codeanalysissys.backend.API;
import com.codeanalysissys.backend.entity.Analysis;
import com.codeanalysissys.backend.entity.task.JSONLAnalysisTask;
import com.codeanalysissys.backend.graphs.ast.ASEdge;
import com.codeanalysissys.backend.graphs.ast.ASNode;
import com.codeanalysissys.backend.graphs.ast.AbstractSyntaxTree;
import com.codeanalysissys.backend.graphs.cfg.CFNode;
import com.codeanalysissys.backend.graphs.cfg.CFEdge;
import com.codeanalysissys.backend.graphs.cfg.ControlFlowGraph;
import com.codeanalysissys.backend.graphs.pdg.*;
import com.codeanalysissys.backend.java.JavaASTBuilder;
import com.codeanalysissys.backend.java.JavaCDGBuilder;
import com.codeanalysissys.backend.java.JavaCFGBuilder;
import com.codeanalysissys.backend.java.JavaDDGBuilder;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.codeanalysissys.backend.python.PythonCDGBuilder;
import com.codeanalysissys.backend.python.PythonASTBuilder;
import com.codeanalysissys.backend.python.PythonCFGBuilder;
import com.codeanalysissys.backend.python.PythonDDGBuilder;
import com.codeanalysissys.backend.golang.*;
import com.codeanalysissys.backend.ruby.*;
import com.codeanalysissys.backend.javascript.*;
import com.codeanalysissys.backend.cpp.*;
import ghaffarian.graphs.Edge;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.FileReader;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import static com.codeanalysissys.backend.service.frame.impl.FileServiceImpl.ANALYSE_SUCCESS;

@Component
@Slf4j
public class CodeAnalysisUtils {
    @Autowired
    private RedisCache redisCache;

    @Autowired
    AnalysisArgsBuilder analysisArgsBuilder;

    // 创建固定线程数的线程池，线程数为 CPU 核心数 * 2
    private static final int THREAD_COUNT = Runtime.getRuntime().availableProcessors() * 2;
    private static final ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);

    private static void wrapToMap(Map<String, Set<String>> map, String code2, String code3) {
        if (map.get(code2) == null) {
            Set<String> set = new HashSet<>();
            set.add(code3);
            map.put(code2, set);
        } else {
            map.get(code2).add(code3);
        }
    }

    // To deal with the AST Node
    private static void wrapToMap(Map<String, Set<String>> map, ASNode srcNode, ASNode tarNode) {
        String srcContent = (srcNode.getCode() == null || srcNode.getCode() == "") ? srcNode.getProperty("type").toString() : srcNode.getCode();
        String tarContent = (tarNode.getCode() == null || tarNode.getCode() == "") ? tarNode.getProperty("type").toString() : tarNode.getCode();
        srcContent = srcNode.getId() + ":" + srcContent;
        tarContent = tarNode.getId() + ":" + tarContent;
        if (map.get(srcContent) == null) {
            Set<String> set = new HashSet<>();
            set.add(tarContent);
            map.put(srcContent, set);
        } else {
            map.get(srcContent).add(tarContent);
        }
    }

    /**
     * 解析上传的文件.jsonl 多线程版本
     *
     * @param uploadFile
     */
    public void analyseJsonlV2(UploadFile uploadFile) {
        String filePath = uploadFile.getFilePath();
        // 获取 jsonl 的文件路径,然后再重新写回文件
        File file = new File(filePath);
        String outDir = file.getParent();
        if (!file.exists()) {
            log.error("文件不存在");
        }

        // 拼接输出文件的路径，输出文件文件名为：源文件名 + "_result.jsonl"
        String outFilePath = outDir + System.getProperty("file.separator") + file.getName().split("\\.")[0] + "_result.jsonl";
        // 创建输出文件
        File outFile = new File(outFilePath);

        try (BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
             BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(outFile))) {

            String line;
            int lineNum = 0;

            // 逐行读取文件并提交解析任务到线程池
            while ((line = bufferedReader.readLine()) != null) {
                lineNum++;
                // 创建解析任务并提交到线程池
                Runnable task = new JSONLAnalysisTask(line, lineNum, file.getName(), bufferedWriter);
                executorService.submit(task);
            }

        } catch (Exception e) {
            log.error("读取文件异常");
        }


        // 将结果写回源文件后，更新 UploadFile 图结果路径
        // 由于是写回源文件，图结果路径即为源文件路径
        uploadFile.setGraphPath(outFilePath);

        // todo 信息提取

        // todo 提取后存本地并更新 UploadFile 分析结果路径

        // 将更新的 UploadFile 对象存入 Redis
        uploadFile.setState(ANALYSE_SUCCESS);
        redisCache.setCacheObject(uploadFile.getFileId(), uploadFile, 1, TimeUnit.DAYS);
    }


    /**
     * 解析上传的文件.jsonl
     *
     * @return
     */
    public void analyseJsonlV1(UploadFile uploadFile) {
        String filePath = uploadFile.getFilePath();
        // 获取 jsonl 的文件路径,然后再重新写回文件
        File file = new File(filePath);
        String outDir = file.getParent();
        if (!file.exists()) {
            log.error("文件不存在");
        }
        // 拼接输出文件的路径，输出文件文件名为：源文件名 + "_result.jsonl"
        String outFilePath = outDir + System.getProperty("file.separator") + file.getName().split("\\.")[0] + "_result.jsonl";
        // 创建输出文件
        File outFile = new File(outFilePath);
        // 分别记录 CFG,CDG,DDG,AST
        Map<String, Set<String>> map1 = new HashMap<>(256);
        Map<String, Set<String>> map2 = new HashMap<>(256);
        Map<String, Set<String>> map3 = new HashMap<>(256);
        Map<String, Set<String>> map4 = new HashMap<>(256);

        try (BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
             BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(outFile))) {
            DecimalFormat formatter = new DecimalFormat("##.##%");
            String line;
            int lineNum = 0;

            while ((line = bufferedReader.readLine()) != null) {
                // 读取行号自增 1
                lineNum = lineNum + 1;

                // 提取行内信息，包括路径、类名和语言
                JSONObject jsonObj = JSON.parseObject(line);
                String path = jsonObj.getString("path");
                String className = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf("."));
                String selectLanguage = jsonObj.getString("language");

                // 根据语言选择对应的解析器
                switch (selectLanguage) {
                    case "java" -> {
                        // 执行与 Java 相关的代码,首先为 csn 代码加上对应的类名
                        String code = "class " + className + " {\n" + jsonObj.getString("code") + "\n}";
                        try {
                            try {
                                // 解析 CFG
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
                                ControlFlowGraph cfg = JavaCFGBuilder.build(codeStream);
                                // 打包 CFG
                                packCFG(map1, jsonObj, cfg);
                            } catch (Throwable ex) {

                            }
                            try {
                                // 解析 DDG
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
                                DataDependenceGraph ddg = JavaDDGBuilder.build(codeStream);
                                // 打包 DDG
                                packDDG(map3, jsonObj, ddg);
                            } catch (Throwable ex) {
                            }
                            try {
                                // 解析 CDG
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
                                ControlDependenceGraph cdg = JavaCDGBuilder.build(codeStream);
                                // 打包 CDG
                                packCDG(map2, jsonObj, cdg);
                            } catch (Throwable ex) {
                            }
                            try {
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
                                AbstractSyntaxTree ast = JavaASTBuilder.build(codeStream);
                                // 打包AST
                                packAST(map4, jsonObj, ast);
                            } catch (Throwable ex) {
                            }
                            bufferedWriter.write(jsonObj.toJSONString());
                            bufferedWriter.newLine();
                        } catch (Throwable ex) {
                        } finally {
                            map1.clear();
                            map2.clear();
                            map3.clear();
                            map4.clear();
                        }
                    }

                    case "python" -> {
                        // 执行与Python相关的代码,首先为csn代码加上对应的类名
                        String code = jsonObj.getString("code");
                        try {
                            try {
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
                                ControlFlowGraph cfg = PythonCFGBuilder.build(codeStream);
                                // 打包CFG
                                packCFG(map1, jsonObj, cfg);
                            } catch (Throwable ex) {
                            }
                            try {
                                byte[] bytes2 = code.getBytes();
                                InputStream codeStream2 = new ByteArrayInputStream(bytes2);
                                DataDependenceGraph ddg = PythonDDGBuilder.build(codeStream2);
                                // 打包DDG
                                packDDG(map3, jsonObj, ddg);
                            } catch (Throwable ex) {
                            }
                            try {
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
                                ControlDependenceGraph cdg = PythonCDGBuilder.build(codeStream);
                                // 打包CDG
                                packCDG(map2, jsonObj, cdg);
                            } catch (Throwable ex) {
                            }
                            try {
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
                                AbstractSyntaxTree ast = PythonASTBuilder.build(codeStream);
                                // 打包AST
                                packAST(map4, jsonObj, ast);
                            } catch (Throwable ex) {
                            }
                            bufferedWriter.write(jsonObj.toJSONString());
                            bufferedWriter.newLine();
                        } catch (Throwable ex) {
                        } finally {
                            map1.clear();
                            map2.clear();
                            map3.clear();
                            map4.clear();
                        }
                    }

                    case "go" -> {
                        // 执行与Java相关的代码,首先为csn代码加上对应的类名
                        String code = jsonObj.getString("code");
                        try {
                            try {
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
                                ControlFlowGraph cfg = GoCFGBuilder.build(codeStream);
                                // 打包CFG
                                packCFG(map1, jsonObj, cfg);
                            } catch (Throwable ex) {
                            }
                            try {
                                byte[] bytes2 = code.getBytes();
                                InputStream codeStream2 = new ByteArrayInputStream(bytes2);
                                DataDependenceGraph ddg = GoDDGBuilder.build(codeStream2);
                                // 打包DDG
                                packDDG(map3, jsonObj, ddg);
                            } catch (Throwable ex) {
                            }
                            try {
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
                                ControlDependenceGraph cdg = GoCDGBuilder.build(codeStream);
                                // 打包CDG
                                packCDG(map2, jsonObj, cdg);
                            } catch (Throwable ex) {
                            }
                            try {
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
                                AbstractSyntaxTree ast = GoASTBuilder.build(codeStream);
                                // 打包AST
                                packAST(map4, jsonObj, ast);
                            } catch (Throwable ex) {
                            }
                            bufferedWriter.write(jsonObj.toJSONString());
                            bufferedWriter.newLine();
                        } catch (Throwable ex) {
                        } finally {
                            map1.clear();
                            map2.clear();
                            map3.clear();
                            map4.clear();
                        }
                    }

                    case "ruby" -> {
                        // 执行与Java相关的代码,首先为csn代码加上对应的类名
                        String code = jsonObj.getString("code");
                        try {
                            try {
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
                                ControlFlowGraph cfg = RubyCFGBuilder.build(codeStream);
                                // 打包CFG
                                packCFG(map1, jsonObj, cfg);
                            } catch (Throwable ex) {
                            }
                            try {
                                byte[] bytes2 = code.getBytes();
                                InputStream codeStream2 = new ByteArrayInputStream(bytes2);
                                DataDependenceGraph ddg = RubyDDGBuilder.build(codeStream2);
                                // 打包DDG
                                packDDG(map3, jsonObj, ddg);
                            } catch (Throwable ex) {
                            }
                            try {
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
                                ControlDependenceGraph cdg = RubyCDGBuilder.build(codeStream);
                                // 打包CDG
                                packCDG(map2, jsonObj, cdg);
                            } catch (Throwable ex) {
                            }
                            try {
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
                                AbstractSyntaxTree ast = RubyASTBuilder.build(codeStream);
                                // 打包AST
                                packAST(map4, jsonObj, ast);
                            } catch (Throwable ex) {
                            }
                            bufferedWriter.write(jsonObj.toJSONString());
                            bufferedWriter.newLine();
                        } catch (Throwable ex) {
                        } finally {
                            map1.clear();
                            map2.clear();
                            map3.clear();
                            map4.clear();
                        }
                    }

                    case "javascript" -> {
                        // 执行与JavaScript相关的代码
                        String code = jsonObj.getString("code");
                        try {
                            try {
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
                                ControlFlowGraph cfg = JavaScriptCFGBuilder.build(codeStream);
                                // 打包CFG
                                packCFG(map1, jsonObj, cfg);
                            } catch (Throwable ex) {
                            }
                            try {
                                byte[] bytes2 = code.getBytes();
                                InputStream codeStream2 = new ByteArrayInputStream(bytes2);
                                DataDependenceGraph ddg = JavaScriptDDGBuilder.build(codeStream2);
                                // 打包DDG
                                packDDG(map3, jsonObj, ddg);
                            } catch (Throwable ex) {
                            }
                            try {
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
                                ControlDependenceGraph cdg = JavaScriptCDGBuilder.build(codeStream);
                                // 打包CDG
                                packCDG(map2, jsonObj, cdg);
                            } catch (Throwable ex) {
                            }
                            try {
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
                                AbstractSyntaxTree ast = JavaScriptASTBuilder.build(codeStream);
                                // 打包AST
                                packAST(map4, jsonObj, ast);
                            } catch (Throwable ex) {
                            }
                            bufferedWriter.write(jsonObj.toJSONString());
                            bufferedWriter.newLine();
                        } catch (Throwable ex) {
                        } finally {
                            map1.clear();
                            map2.clear();
                            map3.clear();
                            map4.clear();
                        }
                    }

                    case "c++" -> {
                        // 执行与C++相关的代码
                        String code = jsonObj.getString("code");
                        try {
                            try {
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
                                ControlFlowGraph cfg = CppCFGBuilder.build(codeStream);
                                // 打包CFG
                                packCFG(map1, jsonObj, cfg);
                            } catch (Throwable ex) {
                            }
                            try {
                                byte[] bytes2 = code.getBytes();
                                InputStream codeStream2 = new ByteArrayInputStream(bytes2);
                                DataDependenceGraph ddg = CppDDGBuilder.build(codeStream2);
                                // 打包DDG
                                packDDG(map3, jsonObj, ddg);
                            } catch (Throwable ex) {
                            }
                            try {
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
//                                    ControlDependenceGraph cdg = CppCDGBuilder.build(codeStream);
                                ControlDependenceGraph cdg = null;
                                // 打包CDG
                                packCDG(map2, jsonObj, cdg);
                            } catch (Throwable ex) {
                            }
                            try {
                                byte[] bytes = code.getBytes();
                                InputStream codeStream = new ByteArrayInputStream(bytes);
                                AbstractSyntaxTree ast = CppASTBuilder.build(codeStream);
                                // 打包AST
                                packAST(map4, jsonObj, ast);
                            } catch (Throwable ex) {
                            }
                            bufferedWriter.write(jsonObj.toJSONString());
                            bufferedWriter.newLine();
                        } catch (Throwable ex) {
                        } finally {
                            map1.clear();
                            map2.clear();
                            map3.clear();
                            map4.clear();
                        }
                    }

                    // 如果没有匹配的情况，执行默认的代码块
                    default -> log.info("Unsupported programming language.");

                }
            }
            bufferedWriter.flush();

            // 将结果写回源文件后，更新 UploadFile 图结果路径
            // 由于是写回源文件，图结果路径即为源文件路径
            uploadFile.setGraphPath(outFilePath);

            // todo 信息提取

            // todo 提取后存本地并更新 UploadFile 分析结果路径

            // 将更新的 UploadFile 对象存入 Redis
            uploadFile.setState(ANALYSE_SUCCESS);
            redisCache.setCacheObject(uploadFile.getFileId(), uploadFile, 1, TimeUnit.DAYS);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void packCDG(Map<String, Set<String>> map2, JSONObject jsonObj, ControlDependenceGraph cdg) {
        Iterator<Edge<PDNode, CDEdge>> it = cdg.allEdgesIterator();
        while (it.hasNext()) {
            Edge<PDNode, CDEdge> edge = it.next();
            wrapToMap(map2, edge.source.getCode(), edge.target.getCode());
        }
        jsonObj.put("control_dependency", map2);
    }

    private void packAST(Map<String, Set<String>> map4, JSONObject jsonObj, AbstractSyntaxTree ast) {
        Iterator<Edge<ASNode, ASEdge>> it = ast.allEdgesIterator();
        while (it.hasNext()) {
            Edge<ASNode, ASEdge> edge = it.next();
            wrapToMap(map4, edge.source, edge.target);
//            wrapToMap(map4, edge.source.getCode(), edge.target.getCode());
        }
        jsonObj.put("AST_Edge", map4);
    }

    private void packDDG(Map<String, Set<String>> map3, JSONObject jsonObj, DataDependenceGraph ddg) {
        Iterator<Edge<PDNode, DDEdge>> it = ddg.allEdgesIterator();
        while (it.hasNext()) {
            Edge<PDNode, DDEdge> edge = it.next();
            wrapToMap(map3, edge.source.getCode(), edge.target.getCode());
        }
        jsonObj.put("data_dependency", map3);
    }

    private void packCFG(Map<String, Set<String>> map1, JSONObject jsonObj, ControlFlowGraph cfg) {
        Iterator<Edge<CFNode, CFEdge>> it = cfg.allEdgesIterator();
        while (it.hasNext()) {
            Edge<CFNode, CFEdge> edge = it.next();
            wrapToMap(map1, edge.source.getCode(), edge.target.getCode());
        }
        jsonObj.put("cfg_dependency", map1);
    }

    /**
     * 解析上传的文件.java
     *
     * @return
     */
    public void analyseJava() {

    }

    /**
     * 解析上传的文件.py
     *
     * @return
     */
    public void analysePython() {

    }

    public void analyseSingle(UploadFile uploadFile) {
        // 获取文件名和文件路径
        String fileName = uploadFile.getName();
        Path path = Paths.get(uploadFile.getFilePath());
        try {
            // 读取字节数组
            byte[] bytes = Files.readAllBytes(path);

            ArrayList<Analysis> opt = (ArrayList<Analysis>) Arrays.stream(Analysis.values())
                    .filter(analysis -> analysis.getType() < 4)
                    .collect(Collectors.toList());

            // 封装API对象，使用AnalysisArgsBuilder，获取每种图类型的builder
            new API(analysisArgsBuilder
                    .setAnalysisOpt(opt)
                    .setFileName(fileName)
                    .setLanguageType(uploadFile.getLanguage()).build()).getExec().executeSingleFile(bytes, uploadFile.getGraphPath());

            // todo 分析文件的系统性信息

            ObjectMapper objectMapper = new ObjectMapper();
//            ArrayList<String> redisGraphData = new ArrayList<>();
//            for(AbstractProgramGraph graph: graphData) {
//                redisGraphData.add(objectMapper.writeValueAsString(graph));
//            }
            // uploadFile.setGraphData(graphData);

            uploadFile.setState(ANALYSE_SUCCESS);
            redisCache.setCacheObject(uploadFile.getFileId(), uploadFile, 7, TimeUnit.DAYS);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public String getZipRes(UploadFile uploadFile, String format) {
        File targetFolder = new File(uploadFile.getGraphPath(), format);
        if (targetFolder.exists() && targetFolder.isDirectory()) {
            File[] files = targetFolder.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isFile() && file.getName().toLowerCase().endsWith(".zip")) {
                        return file.getAbsolutePath();
                    }
                }
            }
        }

        // 还未生成压缩包
        File zipFile = new File(targetFolder.getAbsolutePath(), "output.zip");
        String zipFilePath = zipFile.getAbsolutePath(); // 压缩包的路径
        try {
            FileOutputStream fos = new FileOutputStream(zipFilePath);
            ZipOutputStream zipOut = new ZipOutputStream(fos);

            // 递归遍历文件夹并添加文件到 ZIP 文件中
            zipFiles(targetFolder, targetFolder.getName(), zipOut, format);

            zipOut.close();
            fos.close();
            return zipFilePath;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static void zipFiles(File fileToZip, String fileName, ZipOutputStream zipOut, String format) throws IOException {
        if (fileToZip.isHidden()) {
            return;
        }

        if (fileToZip.isDirectory()) {
            String[] children = fileToZip.list();
            for (String child : children) {
                zipFiles(new File(fileToZip, child), fileName + File.separator + child, zipOut, format);
            }
        } else {
            if (fileToZip.getName().endsWith("." + format.toLowerCase())) {
                FileInputStream fis = new FileInputStream(fileToZip);
                ZipEntry zipEntry = new ZipEntry(fileName);
                zipOut.putNextEntry(zipEntry);

                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = fis.read(buffer)) != -1) {
                    zipOut.write(buffer, 0, bytesRead);
                }
                fis.close();
            }
        }
    }
}
